﻿using System;
using System.Text.RegularExpressions;
using CardPlay.Model.Primitives;

namespace CardPlay.Model.CardModel
{
    public class DealCardAction : IDealAction
    {
        private Predicate<CardFace> _selector = CardFaceSpecification.Any;

        private DealCardAction()
        {
            Repeat = 1;
            Orientation = CardOrientation.FaceDown;
        }

        public DealCardAction(string target, Predicate<CardFace> selector = null) : this()
        {
            Target = target;
            _selector = selector ?? CardFaceSpecification.Any;
        }
        
        public CardOrientation Orientation
        {
            get; set;
        }

        public uint Repeat
        {
            get; set;
        }

        public uint Step
        {
            get; set;
        }

        public string Target
        {
            get; set; 
        }

        bool IDealAction.Execute(IDealContext context)
        {
            if (context.Step != Step) return false; // Try again...

            for (uint i = 0; i < Repeat && ! context.IsDeckEmpty; ++i)
            {
                context.DealCard(_selector, Orientation, Target);
            }

            return true;
        }

        public static DealCardAction Default
        {
            get { return new DealCardAction(); }
        }

        public static DealCardAction Parse(string script)
        {
            var matches = Regex.Matches(script, DealActionExpression);

            if (matches.Count != 1) throw new FormatException();

            var action = Default;

            var groups = matches[0].Groups;

            try
            {
                var repeatText = groups["repeat"].Value;
                action.Repeat = String.IsNullOrEmpty(repeatText) ? 1 : repeatText == DealExpression.Remaining ? UInt32.MaxValue : UInt32.Parse(repeatText);
            }
            catch (FormatException)
            {
            }

            var orientationText = groups["orientation"].Value;

            action.Orientation = (!String.IsNullOrEmpty(orientationText) && orientationText == "up") 
                ? CardOrientation.FaceUp 
                : CardOrientation.FaceDown;

            var filter = new CardFaceSpecification { Rank = Parse<Rank>(groups["rank"].Value) };

            var suitExpr = groups["suit"].Value;

            if (suitExpr != String.Empty)
            {
                try
                {
                    filter.Suit = Parse<Suit>(suitExpr);
                }
                catch
                {
                    filter.SuitColor = Parse<SuitColor>(suitExpr);
                }
            }

            action._selector = filter;
            return action;
        }

        private static TEnum? Parse<TEnum>(string rank) where TEnum : struct
        {
            return String.IsNullOrEmpty(rank) ? null : (TEnum?)Enum<TEnum>.Parse(rank);
        }

        private static string DealActionExpression
        {
            get
            {
                const string repeatExpression = 
                    @"((?<repeat>(\d{1,3}|remaining)) )?";
                const string cardFaceExpression = 
                    @"face\((?<suit>(hearts|spades|diamonds|clubs|red|black))?\s*(?<rank>\w+)?\)";
                const string orientationExpression = 
                    @"(?<orientation>up|down)";

                return String.Format("{0}(?<selector>{1} )?{2}", repeatExpression, cardFaceExpression, orientationExpression);
            }
        }
    }
}
